#include "OSAL.h"
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDProfile.h"
#include "UsbReceiver.h"
#include "usb_framework.h"
#include "usb_interrupt.h"

byte UsbTaskId;
endPointDesc_t epDesc;

afAddrType_t  keyboard_addr;

uint8 nwk_status = ZInvalidParameter;

const cId_t InCmdList[1] = { CMD_IN };
const cId_t OutCmdList[1] = { CMD_OUT };

uint8 recvValueBuf[8] = {0};
uint8 clear[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint8 Continue = 0;

const SimpleDescriptionFormat_t  simpleDesc = 
{
  ENDPOINT_ID,
  PROFILE_ID,
  DEV_COOR_ID,
  DEV_COOR_VER,
  0,
  1,
  (cId_t*)InCmdList,
  1,
  (cId_t*)OutCmdList
};

void send_to_usb(uint8* data);
void StartConfirm(uint8 status);
void SendDataConfirm( uint8 handle, uint8 status );
void BindConfirm( uint16 commandId, uint8 status );
void ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData);




void SendDataConfirm( uint8 handle, uint8 status ){}
void BindConfirm( uint16 commandId, uint8 status ){}

void StartConfirm(uint8 status) {
  if(status == ZSuccess){
    nwk_status = ZSuccess;
    LED_GREEN_ON();
    keyboard_addr.addrMode = Addr16Bit;
    keyboard_addr.endPoint = ENDPOINT_ID;
  }
}

void ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData) {
  if(command == CMD_IN && len == 8) {
    osal_memcpy(recvValueBuf, pData, len);
    osal_set_event(UsbTaskId, SEND_TO_USB_EVENT);
    osal_start_timerEx(UsbTaskId, AUTO_CLEAR_EVENT, 100);
    Continue = 1;
  }
}

void send_to_usb(uint8* data) 
{
    halIntState_t intState;
    HAL_ENTER_CRITICAL_SECTION( intState );
    uint8 ep = USBFW_GET_SELECTED_ENDPOINT();
    USBFW_SELECT_ENDPOINT(1);
    if (USBFW_IN_ENDPOINT_DISARMED()) {
      usbfwWriteFifo(&USBF1, 8, data);
      USBFW_ARM_IN_ENDPOINT();
    }
    USBFW_SELECT_ENDPOINT(ep);
    HAL_EXIT_CRITICAL_SECTION( intState );
}

void Usb_Task_Init( byte task_id ){
  
  UsbTaskId = task_id;

  epDesc.task_id = &UsbTaskId;
  epDesc.endPoint = simpleDesc.EndPoint;
  epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&simpleDesc;
  epDesc.latencyReq = noLatencyReqs;
  afRegister( &epDesc );
  // Turn off match descriptor response
  afSetMatch(epDesc.simpleDesc->EndPoint, FALSE);

  ZDO_RegisterForZDOMsg( UsbTaskId, NWK_addr_rsp );
  ZDO_RegisterForZDOMsg( UsbTaskId, Match_Desc_rsp );

  ZDOInitDevice(10);
  
  usbfwData.pEpInStatus[0] = EP_MANUAL_TX;
  
}


uint16 Usb_ProcessEvent( byte task_id, uint16 events ){
 
  osal_event_hdr_t *pMsg;
  afIncomingMSGPacket_t *pMSGpkt;
  afDataConfirm_t *pDataConfirm;
  
  if ( events & SYS_EVENT_MSG )
  {
    pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id );
    
    while ( pMsg )
    {
      switch ( pMsg->event )
      {
        case ZDO_CB_MSG:
          break;

        case AF_DATA_CONFIRM_CMD:
          pDataConfirm = (afDataConfirm_t *) pMsg;
          SendDataConfirm( pDataConfirm->transID, pDataConfirm->hdr.status );
          break;

        case AF_INCOMING_MSG_CMD:
          pMSGpkt = (afIncomingMSGPacket_t *) pMsg;
          ReceiveDataIndication( pMSGpkt->srcAddr.addr.shortAddr, pMSGpkt->clusterId,
                                 pMSGpkt->cmd.DataLength, pMSGpkt->cmd.Data);
          break;

        case ZDO_STATE_CHANGE:
          if (pMsg->status == DEV_END_DEVICE ||
              pMsg->status == DEV_ROUTER ||
              pMsg->status == DEV_ZB_COORD )
          {
            StartConfirm( ZSuccess );
          }
          else  if (pMsg->status == DEV_HOLD ||
                  pMsg->status == DEV_INIT)
          {
            StartConfirm( ZInit );
          }
          break;

        case ZDO_MATCH_DESC_RSP_SENT:
          break;
  
        default:
          break;
      }
      osal_msg_deallocate( (uint8 *) pMsg );
      pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id );
    }
    events ^= SYS_EVENT_MSG;
  }
  if ( events & SEND_TO_USB_EVENT ) {
    send_to_usb(recvValueBuf);
    Continue = 0;
    events ^= SEND_TO_USB_EVENT;
  }
  
  if ( events & AUTO_CLEAR_EVENT ) {
    if(Continue == 0) {
      clear[0] = recvValueBuf[0];
      send_to_usb(clear);
      events ^= AUTO_CLEAR_EVENT;
    }
  }
  
  return events;
}
